# -*- coding: utf8 -*-
##############################################################
#             Copyright (C), 2009-2013, ali
#
# FileName:   SDTRunner.py
# Author:     yixun.zengyx
# Version:    0.1
#
# Description:
#   SDTRunner
#
#
# Function List:
#
#
# History:
#     <Author/Maintainer>      <Date>      <Modification>
#       yixun.zengyx        2013/03/25    Create this file
#############################################################

import os
import os.path
import sys
import imp,importlib
import SDTestBase
import datetime
import traceback
import time
from Lib.SDTCode import *
from Lib.Color import print_colorful
from doctest import TestResults
import SDTReport
import re
import networkx as nx
import_pygraphviz = True
try:
    import pygraphviz as pgv
except:
    import_pygraphviz = False
import pdb
# from Lib.RequestClient import CharSetParam

class SDTLoader():
    def __init__(self,package=None,imp_list=['*']):
        self.package=package
        self.imp=imp_list   
    
    def getobject(self,package=None):
        if type(package) is str:
            self.package=package
        importret = __import__(self.package,globals(),locals(),self.imp,-1)
        return importret
    def getClassInstance(self,classstr,*args):
        return getattr(self.getobject(),classstr)(*args)   
    def execfunc(self,method,*args):
        return getattr(self.getobject(),method)(*args)
    def execMethod(self,instance,method,*args):
        return getattr(instance,method)(*args)
    
    
    @staticmethod
    def all_members(aClass):
        try:
            #new type class
            mro = list(aClass.__mro__)
        except AttributeError:
            #old type class
            def getmro(aClass,recurse):
    #            print 'getmro:',aClass.__name__
                mro = [aClass]
                for base in aClass.__bases__:
                    mro.extend(recurse(base,recurse.__name__))
                return mro
            def getmro1(aClass):
                mro = [aClass]
                for base in aClass.__bases__:
                    mro.extend(getmro1(base))
                return mro
            mro = getmro(aClass,getmro)
    #        mro = getmro1(aClass)
    
        mro.reverse()
        print aClass.__name__," mro:",mro
        members = {}
        for someClass in mro:
            members.update(vars(someClass))
        return members

class SDTRunner:
    def __init__(self,ResultLog=None,Assert=None):
        self.Loader=SDTLoader()
        self.Logger = ResultLog
        self.Assert = Assert
        self.module = None
        self.current_tc_class = None
        self.tctotal = 0
        self.haserror = False
        
    def Load(self,module=None,classname=None,testname=None):
        if module is not None:
            self.module = module
        if type(testname) is str:
            testname = testname.split(",")
        modulestate = self.module.next()
        if modulestate==PackageImportSuccess:
            self.current_tc_class = SDTReport.TestClassInfo(self.module.ModuleIns[self.module.CurrentModule],classname)
            class_state =  self.current_tc_class.next()
            if (type(testname) is list) and (class_state==GetTcClassIsSuccess):
                self.current_tc_class.update(testname)
            return class_state
        return modulestate

    
    def Count(self,Group=None):
        self.Show(Group,isprint=False)
    
    def Show(self,Group=None,isprint=True,SdGraphicPath="./dot/"):
        if self.module==None:
            return
        while self.module.CurrentModule is not None:
            tcdatacount = 1
            if isprint:
                print_colorful('green', True, "TestModule Name:<"+self.module.CurrentModule+">")
            classname = self.current_tc_class.CurrentClass
            ds = self.current_tc_class.TestDataSource[classname]
            if type(ds) is list:
                tcdatacount = len(ds)
            classins = None
            while classname is not None:
                if isprint:
                    print_colorful('green', True, "    TestClass Name:<"+classname+">"+"\r\n    Total of TestData: "+str(tcdatacount))
                classins,tcnamelist = self.current_tc_class.getcurrent()
                if isinstance(classins, SDTestBase.SDTestCaseBase):
                    if type(tcnamelist) is list:
                        for tcname in tcnamelist:
                                self.Loader.execMethod(classins, tcname, "show")
                    if type(classins.EndStatus) is not list:
                        classins.sdlist = nx.all_simple_paths(classins.G,classins.Current_Status.decode("utf-8"),classins.EndStatus.decode("utf-8"))
                    else:
                        for endstr in classins.EndStatus:
                            tempList = nx.all_simple_paths(classins.G,classins.Current_Status.decode("utf-8"),endstr.decode("utf-8"))
                            for temp in tempList:
                                classins.sdlist.append(temp)
                    if isprint:
                        i = 0
                        for item in classins.sdlist:
                            i+=1
                            lstring = '[SDTest%s]:%s'%(i,'->'.join(item))
                            print "        "+lstring
                            self.tctotal = self.tctotal + 1
                        filename = SdGraphicPath+classname.lower()
                        if  import_pygraphviz:
                            nx.write_dot(classins.G, filename+".dot")
                            A=pgv.AGraph(directed=True,strict=True)
                            A.read(filename+".dot")
                            A.layout('dot')
                            A.draw(filename+".png")
                            if os.path.isfile(filename+".dot"):
                                os.remove(filename+".dot")
                else:
                    if type(tcnamelist) is list:
                        if Group==None:
                            for tcname in tcnamelist:
                                if isprint:
                                    print "        "+tcname
                                else:
                                    self.tctotal = self.tctotal + 1
                        else:
                            for tcname in tcnamelist:
                                tObj = self.Loader.execMethod(classins, tcname, "show")
                                if tObj.Group.lower()==Group.lower():
                                    if isprint:
                                        print "        "+tcname
                                    else:
                                        self.tctotal = self.tctotal + 1
                self.current_tc_class.next()
                classname = self.current_tc_class.CurrentClass
                if classname is not None:
                    ds = self.current_tc_class.TestDataSource[classname]
                    if type(ds) is list:
                        tcdatacount = len(ds)
                classins = None
            self.Load()
        if isprint:
            print_colorful('yellow', True,"\r\nThe Total of TestAction : "+ str(self.tctotal))
            
    def Run(self,Group=None,drive="run"):
        if self.module==None:
            return
        self.TestCaseRun(Group,drive)
        
        
    def SdtCaseRun(self,classname,tcnamelist,classins,drive):
        classins.Init()
        if type(tcnamelist) is list:
            for tcname in tcnamelist:
                self.Loader.execMethod(classins, tcname, "show")
            if type(classins.EndStatus) is not list:
                classins.sdlist = list(nx.all_simple_paths(classins.G,classins.Current_Status.decode("utf-8"),classins.EndStatus.decode("utf-8")))
            else:
                for endstr in classins.EndStatus:
                    tempList = list(nx.all_simple_paths(classins.G,classins.Current_Status.decode("utf-8"),endstr.decode("utf-8")))
                    for temp in tempList:
                        classins.sdlist.append(temp)
#             print "classins.sdlist:%s"%(classins.sdlist)
            self.tctotal = self.tctotal + len(classins.sdlist)
        while len(classins.sdlist)>0:
            if classins.fail or classins.error:
                break
            CurrentTC = classins.sdlist.pop(0)
            classins.sdindex += 1
            self.Loader.execMethod(classins,"SetUp")
            while len(CurrentTC)>0:
                classins.Current_Status = CurrentTC.pop(0)
                if len(CurrentTC)>0:
                    classins.Next_Status = CurrentTC[0]
                    tcname = classins.G[classins.Current_Status][classins.Next_Status]["tc"]
                    self.Loader.execMethod(classins,tcname,drive,self.module.CurrentModule+":"+classname,self.Logger,self.Assert,self)
                else:
                    classins.Next_Status = None
                if classins.Current_Status==classins.EndStatus:
                    break
                if type(classins.EndStatus) is list:
                    if classins.Current_Status in classins.EndStatus:
                        break
            self.Loader.execMethod(classins,"TearDown")
        
    def TestCaseRun(self,Group=None,drive="run"):
        while self.module.CurrentModule is not None:
            if self.haserror:
                break
            self.Logger.Info("Enter TestModule Name:"+self.module.CurrentModule)
            classname = self.current_tc_class.CurrentClass
            ds = self.current_tc_class.TestDataSource[classname]
            while classname is not None:
                if self.haserror:
                    break
                self.Logger.Info("Enter TestClass Name:"+classname)
                classins,tcnamelist = self.current_tc_class.getcurrent()
                if isinstance(classins, SDTestBase.SDTestCaseBase):
                    print "classins IData is:%s"%(len(classins.IData))
                    while len(classins.IData)>0:
                        tcnamelistCopy = tcnamelist[:]#完全复制一份list
                        classins.DS = classins.IData.pop(0)
                        self.SdtCaseRun(classname,tcnamelistCopy,classins,drive)
                    self.current_tc_class.next()
                    classname = self.current_tc_class.CurrentClass
                    break
                if type(tcnamelist) is list:
                    if Group==None:
                        if type(ds) is list:
                            tccount = len(tcnamelist)
                            if tccount>0:
                                for curds in ds:
                                    classins.DS = curds
                                    self.Loader.execMethod(classins,"SetUp")
                                    for tcname in tcnamelist:
                                        self.Loader.execMethod(classins,tcname,drive,self.module.CurrentModule+":"+classname,self.Logger,self.Assert,self,Group)
                                    self.Loader.execMethod(classins,"TearDown")
                        else:
                            tccount = len(tcnamelist)
                            if tccount>0:
                                if ds is not None:
                                    classins.DS = ds
                                self.Loader.execMethod(classins,"SetUp")
                                for tcname in tcnamelist:
                                    self.Loader.execMethod(classins,tcname,drive,self.module.CurrentModule+":"+classname,self.Logger,self.Assert,self,Group)
                                self.Loader.execMethod(classins,"TearDown")
                    else:
                        tccount = len(tcnamelist)
                        if type(ds) is list:
                            if tccount>0:
                                for curds in ds:
                                    classins.DS = curds
                                    self.Loader.execMethod(classins,"SetUp")
                                    for tcname in tcnamelist:
                                        self.Loader.execMethod(classins, tcname, drive,self.module.CurrentModule+":"+classname,self.Logger,self.Assert,self,Group)
                                    self.Loader.execMethod(classins,"TearDown")
                        else:
                            if tccount>0:
                                if ds is not None:
                                    classins.DS = ds
                                self.Loader.execMethod(classins,"SetUp")
                                for tcname in tcnamelist:
                                    self.Loader.execMethod(classins, tcname, drive,self.module.CurrentModule+":"+classname,self.Logger,self.Assert,self,Group)
                                self.Loader.execMethod(classins,"TearDown")
                self.current_tc_class.next()
                classname = self.current_tc_class.CurrentClass
                if classname is not None:
                    ds = self.current_tc_class.getdata(classname)
            self.Load()
        
        
        
        
        
    def RunTestCase(self,tObj,tName,tFun,Param=None, Context=None,Group="P1", sFrom=None, sTo=None, Debug=False,Logger=None,Assert=None):
        if self.haserror:
            return
        tObj.Param=SDTReport.HandleData.genData(Param)
        tObj.Group = Group
        tObj.Logger = Logger
        tObj.Assert = Assert
        tObj.Context = Context
        bd = datetime.datetime.now()
        duration=0
        isFail = False
        try:
            if tObj.Logger<>None:
                tObj.Logger.Info("Exec TestName :[%s]"%(tName))
            self.Loader.execMethod(tObj, "BeginTestCase")
            bd = datetime.datetime.now()
            tFun()
            ed = datetime.datetime.now()
            d=ed-bd
            duration=d.days * 24 * 60 * 60 * 1000 + d.seconds * 1000 + d.microseconds / 1000
        except SDTestBase.TestCheck:
            isFail = True
            if isinstance(tObj, SDTestBase.SDTestCaseBase):
                self.fail = True
                Logger.Fail("TestName is:    <SDTestIndex:%s>    :%s->%s,Action is :%s"%(tObj.sdindex,tObj.Current_Status,tObj.Next_Status,tName),True)
            else:
                Logger.Fail(tName+"    <SDTestIndex:0>    ",True)
            info = sys.exc_info()
            print_colorful('red',True,"    TestFail:"+tName)
            self.Loader.execMethod(tObj, "ErrorHandle",info[1],info[:2][1])
            if Debug:
                self.haserror =True
            print_colorful('red',True,info[:2][1])
            for file, lineno, function, text in traceback.extract_tb(info[2]):
                if text==None:
                    continue
                if (function=="RunTestCase") or (function=="<lambda>"):
                    continue
                ept = re.findall("\s*raise TestCheck",text)
                if ept==[]:
                    Logger.Fail("%s line:%s in %s , msg:%s"%(file,lineno, function,text))
            return
        except:
            isFail = True
            if isinstance(tObj, SDTestBase.SDTestCaseBase):
                self.error = True
                Logger.Error("TestName is:    <SDTestIndex:%s>    :%s->%s,Action is :%s"%(tObj.sdindex,tObj.Current_Status,tObj.Next_Status,tName),True)
            else:
                Logger.Error(tName+"    <SDTestIndex:0>    ",True)
            info = sys.exc_info()
            print_colorful('red',True,"    Error:"+tName)
            self.Loader.execMethod(tObj, "ErrorHandle",info[1],info[:2][1])
            if Debug:
                self.haserror =True
                print_colorful('red',True,info[:2][1])
                for file, lineno, function, text in traceback.extract_tb(info[2]):
                    if (function=="RunTestCase") or (function=="<lambda>"):
                        continue
                    Logger.Error("%s line:%s in %s , msg:%s"%(file,lineno, function,text))
            return
        finally:
            try:
#                 CharSetParamResult = CharSetParam.encode(tObj.Param)
                Logger.Info("%s Input Data is: %s\r\n"%(tName,tObj.Param))
                self.Loader.execMethod(tObj, "EndTestCase")
            except:
                isFail = True
                if isinstance(tObj, SDTestBase.SDTestCaseBase):
                    self.error = True
                    Logger.Error("TestName is:    <SDTestIndex:%s>    :%s->%s,Action is :%s"%(tObj.sdindex,tObj.Current_Status,tObj.Next_Status,tName),True)
                else:
                    Logger.Error(tName+"    <SDTestIndex:0>    ",True)
                info = sys.exc_info()
                print_colorful('red',True,"    Error:"+tName)
                self.Loader.execMethod(tObj, "ErrorHandle",info[1],info[:2][1])
                if Debug:
                    self.haserror =True
                    print_colorful('red',True,info[:2][1])
                    for file, lineno, function, text in traceback.extract_tb(info[2]):
                        if (function=="RunTestCase") or (function=="<lambda>"):
                            continue
                        Logger.Error("%s line:%s in %s , msg:%s"%(file,lineno, function,text))
            if (not self.haserror) and (not isFail):
                if not isinstance(tObj, SDTestBase.SDTestCaseBase):
                    Logger.Success(tName+"    <SDTestIndex:0>"+"    duration:"+str(duration)+" ms",True)
                else:
                    Logger.Success("TestName is:    <SDTestIndex:%s>    :%s->%s,Action is :%s    duration:%s ms"%(tObj.sdindex,tObj.Current_Status,tObj.Next_Status,tName,str(duration)),True)
#                     Logger.Success(tName+"    <SDTestIndex:"+str(tObj.sdindex)+">"+"    duration:"+str(duration)+" ms",True)
#         tp,val,td=sys.exc_info()
#         print type(val)==SDTestBase.TestCheck
#         traceback.print_exc()
#         print dir(traceback)

Loader = SDTLoader()
Runner = SDTRunner()

if __name__ == '__main__':
    dyn=SDTLoader('SDTestBase',['*'])
    print dyn
    print dyn.getobject('SDTestBase').__file__
#     ins,testlist=dyn.getClassInstance('TestC')
#     dyn.execMethod(ins,'test1')
    
